﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Windows.Data;
using System.Windows.Input;
using MathematicallySafe.HeadCounter.Commands;
using MathematicallySafe.HeadCounter.Common;
using MathematicallySafe.HeadCounter.Data;
using MathematicallySafe.HeadCounter.Data.Common;
using MathematicallySafe.HeadCounter.Data.Models;
using MathematicallySafe.HeadCounter.Services;
using StructureMap;

namespace MathematicallySafe.HeadCounter.ViewModels
{
    public class RaidViewModel: ViewModelBase
    {
        private static readonly IHeadCounterService HeadCounterService = ObjectFactory.GetInstance<IHeadCounterService>();

        private ObservableCollection<RaidMemberViewModel> _raidMembers;
        private ObservableCollection<NonRaidMemberViewModel> _nonRaiders;
        private ObservableCollection<RaidLootViewModel> _loot;
        private ObservableCollection<RaidLootViewModel> _unassignedLoot;

        private string _selectedRaider;

        private DelegateCommand<string> _alterOutcomeCommand;

        public DateTime Date
        {
            get { return Model.Date; }
        }

        public string Zone
        {
            get { return Model.Zone; }
        }

        public RaidDifficulty Difficulty
        {
            get { return Model.Difficulty; }
        }

        public bool Optional
        {
            get { return Model.Optional; }
        }

        public string SelectedRaider
        {
            get { return _selectedRaider; }
            set
            {
                if (_selectedRaider != value)
                {
                    _selectedRaider = value;

                    if (value == null)
                    {
                        LootView.Filter = null;
                    }
                    else
                    {
                        LootView.Filter = item => ((RaidLootViewModel) item).Model.WinnerIdentity == _selectedRaider;
                    }

                    OnPropertyChanged("SelectedRaiderIdentity");
                }
            }
        }

        public ListCollectionView RaidMembersView { get; private set; }
        public ListCollectionView NonRaidersView { get; private set; }
        public ListCollectionView LootView { get; private set; }
        public ListCollectionView UnassignedLootView { get; private set; }

        public RaidViewModel(Raid model)
        {
            Model = model;

            LoadRaiders();
            LoadNonRaiders();
            LoadLoot();
        }

        protected override void SubscribeEvents()
        {
            Model.AttendanceChanged += AttendanceChanged;
        }

        protected override void UnsubscribeEvents()
        {
            Model.AttendanceChanged -= AttendanceChanged;

            _loot.ForEach(l =>
            {
                l.Model.WinnerChanged -= RaidLootWinnerChanged;
                l.Model.OutcomeChanged -= RaidLootOutcomeChanged;
            });

            _unassignedLoot.ForEach(l =>
            {
                l.Model.WinnerChanged -= RaidLootWinnerChanged;
                l.Model.OutcomeChanged -= RaidLootOutcomeChanged;
            });
        }

        public Raid Model { get; private set; }

        private void LoadRaiders()
        {
            _raidMembers = new ObservableCollection<RaidMemberViewModel>();
            Model.Attendees.ForEach(a => _raidMembers.Add(CreateRaiderViewModel(a)));

            RaidMembersView = new ListCollectionView(_raidMembers);
        }

        private RaidMemberViewModel CreateRaiderViewModel(RaidAttendance attendee)
        {
            var raiderName = attendee.RaidingAs;

            var raidMemberViewModel = new RaidMemberViewModel(raiderName)
            {
                ItemsWonCount = GetLootWonCount(raiderName),
                Minutes = attendee.Minutes
            };

            return raidMemberViewModel;
        }

        private void LoadNonRaiders()
        {
            _nonRaiders = new ObservableCollection<NonRaidMemberViewModel>();

            var nonRaiders = from r in HeadCounterService.Guild.ActiveRaidersByMain
                             where !Model.Raiders.Contains(r) && r.Joined <= Model.Date
                             select r;

            nonRaiders.ForEach(n => _nonRaiders.Add(CreateNonRaiderViewModel(n.Identity)));

            NonRaidersView = new ListCollectionView(_nonRaiders);
        }

        private NonRaidMemberViewModel CreateNonRaiderViewModel(string identity)
        {
            var nonRaidMemberViewModel = new NonRaidMemberViewModel(Model, identity)
                                          {
                                              Attended = Model.IsExtraAttendee(identity)
                                          };

            return nonRaidMemberViewModel;
        }

        public IEnumerable<RaidMemberViewModelBase> AllAttendees
        {
            get
            {
                return _raidMembers.Cast<RaidMemberViewModelBase>().Union(_nonRaiders.Cast<RaidMemberViewModelBase>());
            }
        }

        private int GetLootWonCount(string attendeeName)
        {
            return Model.Loot.Where(l => l.Winner == attendeeName).Count();
        }

        private void LoadLoot()
        {
            _loot = new ObservableCollection<RaidLootViewModel>();
            Model.Loot.Where(l => l.Outcome == LootOutcome.Won).ForEach(lootItem => AddLoot(_loot, lootItem));

            LootView = new ListCollectionView(_loot);

            _unassignedLoot = new ObservableCollection<RaidLootViewModel>();
            Model.Loot.Where(l => l.Outcome != LootOutcome.Won).ForEach(lootItem => AddLoot(_unassignedLoot, lootItem));

            UnassignedLootView = new ListCollectionView(_unassignedLoot);
        }

        private void AddLoot(ICollection<RaidLootViewModel> lootCollection, RaidLoot raidLoot)
        {
            var viewModel = new RaidLootViewModel(raidLoot);
            viewModel.Initialise();

            lootCollection.Add(viewModel);
            
            viewModel.Model.WinnerChanged += RaidLootWinnerChanged;
            viewModel.Model.OutcomeChanged += RaidLootOutcomeChanged;
        }

        void RaidLootWinnerChanged(object sender, WinnerChangedEventArgs e)
        {
            var calculationService = ObjectFactory.GetInstance<ICalculationService>();
            var guild = ObjectFactory.GetInstance<IHeadCounterService>().Guild;

            var affectedRaiders = new List<Raider>();

            var originalWinner = _raidMembers.FirstOrDefault(rm => rm.Name == e.OriginalWinner);
            var newWinner = _raidMembers.FirstOrDefault(rm => rm.Name == e.NewWinner);

            if (originalWinner != null)
            {
                originalWinner.ItemsWonCount--;
                affectedRaiders.Add(guild.FindRaider(originalWinner.Name));
            }

            if (newWinner != null)
            {
                newWinner.ItemsWonCount++;
                affectedRaiders.Add(guild.FindRaider(newWinner.Name));
            }

            calculationService.Process(affectedRaiders);
            OnPropertyChanged("LootView");
        }

        void RaidLootOutcomeChanged(object sender, OutcomeChangedEventArgs e)
        {
            var loot = (RaidLoot) sender;

            var originalCollection = e.OriginalOutcome == LootOutcome.Won ? _loot : _unassignedLoot;
            var newCollection = e.OriginalOutcome == LootOutcome.Won ? _unassignedLoot : _loot;

            var lootViewModel = originalCollection.Where(l => l.Model.Time == loot.Time && l.ID == l.ID).FirstOrDefault();

            originalCollection.Remove(lootViewModel);
            newCollection.Add(lootViewModel);

            OnPropertyChanged("LootView");
            OnPropertyChanged("UnassignedLootSummary");
        }

        public ICommand AlterOutomeCommand
        {
            get
            {
                if (_alterOutcomeCommand == null)
                {
                    _alterOutcomeCommand = new DelegateCommand<string>(AlterOutcome);
                }

                return _alterOutcomeCommand;
            }
        }

        private void AlterOutcome(string outcome)
        {
            var droppedLoot = DragDropManager.DraggedData as RaidLootViewModel;

            if (droppedLoot != null)
            {
                // todo this should be the enum not a string, need to bind to static instances in xaml i guess?
                droppedLoot.Outcome = (LootOutcome)Enum.Parse(typeof(LootOutcome), outcome);
            }
        }

        public string UnassignedLootSummary
        {
            get
            {
                int won = _loot.Count;
                int unassigned = _unassignedLoot.Count;
                int de = _unassignedLoot.Count(loot => loot.Outcome == LootOutcome.Disenchanted);
                int banked = _unassignedLoot.Count(loot => loot.Outcome == LootOutcome.Banked);
                int offspec = _unassignedLoot.Count(loot => loot.Outcome == LootOutcome.Offspec);

                string summary = string.Format("Total = {0}, Won = {1}, D/E = {2}", won + unassigned, won, de);

                if (offspec > 0) summary += string.Format(", Offspec={0}", offspec);
                if (banked > 0) summary += string.Format(", Banked={0}", banked);

                return summary;
            }
        }

        private void AttendanceChanged(Raid raid)
        {
            LoadNonRaiders();
            LoadRaiders();

            OnPropertyChanged("RaidMembersView");
            OnPropertyChanged("NonRaidersView");
        }
    }
}
